#ifndef CEVRECDELEGATES_H_INCLUDED
#define CEVRECDELEGATES_H_INCLUDED

    #include <stdexcept> // std::logic_error("")
    #include "CBaseReceiverModule.h"

    namespace irr
    {

    /// просто массивы ф-ций и запрограммированная реакция по ним
    /// позволяет динамически добавлять/удалять свои ф-ции из рецивера без пересоздания оного
        class CEvRecDelegates : public CBaseReceiverModule
        {
          public:
            /// базовые указатели на ф-ции GUI событий

            struct Base
            {
                virtual void setReceiver( CEvRecDelegates * _self_ ){ self = _self_; }
                Base(): self( NULL ) {}
                virtual ~Base() { }
                protected:
                    CEvRecDelegates * self;
            };

            struct BaseGUIFunc : Base  // функторы
            {
                virtual bool operator()( const SEvent::SGUIEvent & _GUIEvents_ ) = 0;
                virtual ~BaseGUIFunc() { if( !self ) return; if( !self->eraseFuncFromEvent( this ) ) self->eraseElemHandler(this); }
            };

            struct BaseJoystickFunc : Base
            {
                virtual bool operator()( const SEvent::SJoystickEvent & _JoysEvent_ ) = 0;
                virtual ~BaseJoystickFunc() { if( !self ) return; self->eraseFuncFromEvent( this ); }
            };

            struct BaseKeyFunc : Base
            {
                virtual bool operator()( const SEvent::SKeyInput & _KeyEvents_ ) = 0;
                virtual ~BaseKeyFunc() { if( !self ) return; self->eraseFuncFromEvent( this ); }
            };

            struct BaseLogEventFunc : Base
            {
                virtual bool operator()( const SEvent::SLogEvent & _LogEvents_ ) = 0;
                virtual ~BaseLogEventFunc() { if( !self ) return; self->eraseFuncFromEvent( this ); }
            };

            struct BaseMouseFunc : Base
            {
                virtual bool operator()( const SEvent::SMouseInput & _MouseEvents_ ) = 0;
                virtual ~BaseMouseFunc() { if( !self ) return; self->eraseFuncFromEvent( this ); }
            };

            struct BaseUserFunc : Base
            {
                virtual bool operator()( const SEvent::SUserEvent & _UserEvents_ ) = 0;
                virtual ~BaseUserFunc() { if( !self ) return; self->eraseFuncFromEvent( this ); }
            };

          protected:
            core::array<BaseGUIFunc*> guie_funcs;
            core::array<BaseJoystickFunc*> joysticke_funcs;
            core::array<BaseKeyFunc*> keye_funcs;
            core::array<BaseLogEventFunc*> loge_funcs;
            core::array<BaseMouseFunc*> mousee_funcs;
            core::array<BaseUserFunc*> usere_funcs;

            /// для массива реакций для конкретных елементов гуи.
            struct GUIElemHandler
            {
                BaseGUIFunc  * pf;
                gui::EGUI_EVENT_TYPE EventType;
                s32 GUIElem_id;
            };

            core::array<GUIElemHandler> guiElem_handlers;

          public:

          void addGUIElemHandler( s32 _ElemID_, gui::EGUI_EVENT_TYPE _eventType_, BaseGUIFunc  * _pf_  )
          {
                _pf_->setReceiver( this );
                guiElem_handlers.push_back( GUIElemHandler() );
                guiElem_handlers.getLast().pf = _pf_;
                guiElem_handlers.getLast().EventType = _eventType_;
                guiElem_handlers.getLast().GUIElem_id = _ElemID_;
          }

          void addGUIElemHandler( gui::IGUIElement * _toMe_, gui::EGUI_EVENT_TYPE _eventType_, BaseGUIFunc  * _pf_  )
          {
                addGUIElemHandler(_toMe_->getID(),_eventType_,_pf_ );
          }

          bool eraseElemHandler( BaseGUIFunc * erase_pf )
          {
            for ( u32 i=0; i<guiElem_handlers.size(); ++i )
                if ( guiElem_handlers[i].pf == erase_pf ) { guiElem_handlers.erase(i); return true; }

            return false;
          }

            /// сравнение по типам
                template < class T, class U > struct if_types
                {
                    enum { equal = false };
                };

                template < class T > struct if_types< T, T >
                {
                    enum{ equal = true };
                };

          /// вспомогательная, проверяет тип, чтобы небыло обшибок
          template< class T >
          void addFuncToEvent( core::stringc _eventName_, T new_pf ) /// key log mouse user gui joystick
          {
                /// все символы строки - в нижний регистр
                struct // безымянная структура
                {
                    void operator()( core::stringc & _toLower_ )
                    {
                        for( size_t i = 0; i< _toLower_.size(); i++ )
                            _toLower_[i] = tolower( _toLower_[i] );
                    }
                } toLowerAll;

                toLowerAll(_eventName_);

                bool isSet = false;
                if( _eventName_ == "key" && if_types< T,BaseKeyFunc* >::equal ) { addFuncToEventVoid( _eventName_,(void*)new_pf ); isSet = true; }
                if( _eventName_ == "log" && if_types< T,BaseLogEventFunc* >::equal ) { addFuncToEventVoid( _eventName_,(void*)new_pf ); isSet = true; }
                if( _eventName_ == "mouse" && if_types< T,BaseMouseFunc* >::equal ) { addFuncToEventVoid( _eventName_,(void*)new_pf ); isSet = true; }
                if( _eventName_ == "user" && if_types< T,BaseUserFunc* >::equal )  { addFuncToEventVoid( _eventName_,(void*)new_pf ); isSet = true; }
                if( _eventName_ == "gui" && if_types< T,BaseGUIFunc* >::equal ) { addFuncToEventVoid( _eventName_,(void*)new_pf ); isSet = true; }
                if( ( (_eventName_ == "joystick") || (_eventName_ == "joys") || (_eventName_ == "joy")
                    || (_eventName_ == "jstc") || (_eventName_ == "jstk") )
                    && if_types< T,BaseJoystickFunc* >::equal )
                    { addFuncToEventVoid( _eventName_,(void*)new_pf ); isSet = true; }

                if ( !isSet ) throw std::logic_error("addFuncToEvent(...) func: Element not set! _eventName_ is not find or < T,T > not equal!");
          }

            void addFuncToEventVoid( core::stringc _eventName_, void * new_pf ) /// key log mouse user gui joystick
            {
                if( _eventName_ == "key" ) keye_funcs.push_back( (BaseKeyFunc*) new_pf );
                if( _eventName_ == "log" ) loge_funcs.push_back( (BaseLogEventFunc*) new_pf );
                if( _eventName_ == "mouse" ) mousee_funcs.push_back( (BaseMouseFunc*) new_pf );
                if( _eventName_ == "user" )  usere_funcs.push_back( (BaseUserFunc*) new_pf );
                if( _eventName_ == "gui" ) guie_funcs.push_back( (BaseGUIFunc*) new_pf );
                if( (_eventName_ == "joystick") || (_eventName_ == "joys") || (_eventName_ == "joy") || (_eventName_ == "jstc") )
                    joysticke_funcs.push_back( (BaseJoystickFunc*) new_pf );

                // setReceiver() ОБЯЗАНА быть ВИРТУАЛЬНОЙ(иначе будет вышибать )
                ((Base*)new_pf)->setReceiver( this );
            }

            bool eraseFuncFromEvent( void * erase_pf ) /// удаление производится по адресу удаляемой ф-ции
            {
                for ( u32 i=0; i<guie_funcs.size(); ++i )
                    if ( guie_funcs[i] == erase_pf ) { guie_funcs.erase(i); return true; }

                for ( u32 i=0; i<joysticke_funcs.size(); ++i )
                    if ( joysticke_funcs[i] == erase_pf ) { joysticke_funcs.erase(i); return true; }

                for ( u32 i=0; i<keye_funcs.size(); ++i )
                    if ( keye_funcs[i] == erase_pf ) { keye_funcs.erase(i); return true; }

                for ( u32 i=0; i<loge_funcs.size(); ++i )
                    if ( loge_funcs[i] == erase_pf ) { loge_funcs.erase(i); return true; }

                for ( u32 i=0; i<mousee_funcs.size(); ++i )
                    if ( mousee_funcs[i] == erase_pf ) { mousee_funcs.erase(i); return true; }

                for ( u32 i=0; i<usere_funcs.size(); ++i )
                    if ( usere_funcs[i] == erase_pf ) { usere_funcs.erase(i); return true; }

                return false;
            }

            virtual bool OnEvent( const SEvent & event )
            {
                irr::CBaseReceiverModule::OnEvent( event );

                switch( event.EventType )
                {
                    case EET_GUI_EVENT :
                        {
                            // вызывать обработчики для конкретных GUI элементов
                            for ( u32 i=0; i<guiElem_handlers.size(); ++i )
                                if ( event.GUIEvent.EventType == guiElem_handlers[i].EventType
                                     && event.GUIEvent.Caller->getID() == guiElem_handlers[i].GUIElem_id )

                                    if( (*guiElem_handlers[i].pf)( event.GUIEvent ) ) return true;

                            for ( u32 i=0; i<guie_funcs.size(); ++i )
                                if( (*guie_funcs[i])( event.GUIEvent ) ) return true;
                        }
                        break;

                    case EET_JOYSTICK_INPUT_EVENT :
                        for ( u32 i=0; i<joysticke_funcs.size(); ++i )
                            if( (*joysticke_funcs[i])( event.JoystickEvent ) ) return true;
                        break;

                    case EET_KEY_INPUT_EVENT :
                        for ( u32 i=0; i<keye_funcs.size(); ++i )
                            if( (*keye_funcs[i])( event.KeyInput ) ) return true;
                        break;

                    case EET_LOG_TEXT_EVENT :
                        for ( u32 i=0; i<loge_funcs.size(); ++i )
                            if( (*loge_funcs[i])( event.LogEvent ) ) return true;
                        break;

                    case EET_MOUSE_INPUT_EVENT :
                        for ( u32 i=0; i<mousee_funcs.size(); ++i )
                            if( (*mousee_funcs[i])( event.MouseInput ) ) return true;
                        break;

                    case EET_USER_EVENT :
                        for ( u32 i=0; i<usere_funcs.size(); ++i )
                            if( (*usere_funcs[i])( event.UserEvent ) ) return true;
                        break;

                    default:
                     return false;
                }

                return false;
            } // OnEvent()
        };
    } // namespace irr


#endif // CEVRECDELEGATES_H_INCLUDED
